from flask import Flask, request, jsonify, render_template
import parselmouth
import numpy as np
import os
import base64
import json
import tempfile
from flask_cors import CORS
import wave
import struct
# 移除matplotlib相关导入
# import matplotlib
# matplotlib.use('Agg')  # 非交互式后端
# import matplotlib.pyplot as plt
from io import BytesIO
import base64
from audio_repository import StandardAudioRepository
from tone_analyzer import analyze_audio_data
from db_config import execute_query

# 导入API接口
from tone_api import (
    get_all_characters,
    get_characters_by_tone,
    analyze_tone,
    get_character_audio
)

app = Flask(__name__)
CORS(app)  # 启用跨域请求支持

# 注册API接口
app.add_url_rule('/api/characters', view_func=get_all_characters, methods=['GET'])
app.add_url_rule('/api/characters/by-tone/<int:tone>', view_func=get_characters_by_tone, methods=['GET'])
app.add_url_rule('/api/tone-analysis', view_func=analyze_tone, methods=['POST'])
app.add_url_rule('/api/character-audio/<character>/<pinyin>', view_func=get_character_audio, methods=['GET'])

@app.route('/')
def hello_world():
    return render_template('index.html')

@app.route('/api/analyze-pitch', methods=['POST'])
def analyze_pitch():
    try:
        data = request.json
        
        # 检查是否有音频数据
        if 'audioData' not in data:
            return jsonify({'error': '未找到音频数据'}), 400
            
        # 获取音频参数
        audio_data = data.get('audioData')
        sample_rate = data.get('sampleRate', 16000)
        channels = data.get('channels', 1)
        
        # 可选参数
        pitch_floor = float(data.get('pitchFloor', 75.0))  # 基频下限，默认75Hz
        pitch_ceiling = float(data.get('pitchCeiling', 500.0))  # 基频上限，默认500Hz
        
        # 将Base64编码的音频数据解码
        if isinstance(audio_data, str) and audio_data.startswith('data:audio'):
            # 处理dataURL格式
            audio_data = audio_data.split(',')[1]
            
        decoded_data = base64.b64decode(audio_data)
        
        # 创建临时WAV文件
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
            temp_path = temp_file.name
            
            # 如果数据是PCM格式，转换为WAV
            if data.get('format') == 'pcm':
                # 创建WAV文件头
                with wave.open(temp_path, 'wb') as wav_file:
                    wav_file.setnchannels(channels)
                    wav_file.setsampwidth(2)  # 16位音频
                    wav_file.setframerate(sample_rate)
                    wav_file.writeframes(decoded_data)
            else:
                # 直接写入数据（假设已经是WAV格式）
                temp_file.write(decoded_data)
        
        # 使用Parselmouth分析音频
        sound = parselmouth.Sound(temp_path)
        
        # 创建带参数的Pitch对象
        pitch = sound.to_pitch(pitch_floor=pitch_floor, pitch_ceiling=pitch_ceiling)
        
        # 获取基频和时间信息
        pitch_values = pitch.selected_array['frequency'].tolist()
        timestamps = pitch.xs().tolist()
        
        # 过滤掉未检测到的基频点（值为0）
        filtered_data = [
            {"time": time, "frequency": freq} 
            for time, freq in zip(timestamps, pitch_values) 
            if freq > 0
        ]
        
        # 计算统计信息
        valid_freqs = [item["frequency"] for item in filtered_data]
        
        if not valid_freqs:
            stats = {
                "mean": 0,
                "min": 0,
                "max": 0,
                "count": 0,
                "median": 0,
                "std": 0
            }
        else:
            stats = {
                "mean": float(np.mean(valid_freqs)),
                "min": float(np.min(valid_freqs)),
                "max": float(np.max(valid_freqs)),
                "count": len(valid_freqs),
                "median": float(np.median(valid_freqs)),
                "std": float(np.std(valid_freqs))
            }
        
        # 清理临时文件
        os.unlink(temp_path)
        
        return jsonify({
            "status": "success",
            "data": filtered_data,
            "stats": stats
        })
    
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/analyze-audio-full', methods=['POST'])
def analyze_audio_full():
    """完整分析音频，返回归一化点和拟合参数"""
    try:
        data = request.json
        
        # 检查是否有音频数据
        if 'audioData' not in data:
            return jsonify({'error': '未找到音频数据'}), 400
            
        # 首先获取基频数据
        audio_data = data.get('audioData')
        sample_rate = data.get('sampleRate', 16000)
        channels = data.get('channels', 1)
        format_type = data.get('format', 'wav')
        pitch_floor = float(data.get('pitchFloor', 75.0))
        pitch_ceiling = float(data.get('pitchCeiling', 500.0))
        
        # 将Base64编码的音频数据解码
        if isinstance(audio_data, str) and audio_data.startswith('data:audio'):
            # 处理dataURL格式
            audio_data = audio_data.split(',')[1]
            
        decoded_data = base64.b64decode(audio_data)
        
        # 创建临时WAV文件
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
            temp_path = temp_file.name
            
            # 如果数据是PCM格式，转换为WAV
            if format_type == 'pcm':
                # 创建WAV文件头
                with wave.open(temp_path, 'wb') as wav_file:
                    wav_file.setnchannels(channels)
                    wav_file.setsampwidth(2)  # 16位音频
                    wav_file.setframerate(sample_rate)
                    wav_file.writeframes(decoded_data)
            else:
                # 直接写入数据（假设已经是WAV格式）
                temp_file.write(decoded_data)
        
        # 使用Parselmouth分析音频
        sound = parselmouth.Sound(temp_path)
        
        # 创建带参数的Pitch对象
        pitch = sound.to_pitch(pitch_floor=pitch_floor, pitch_ceiling=pitch_ceiling)
        
        # 获取基频和时间信息
        pitch_values = pitch.selected_array['frequency'].tolist()
        timestamps = pitch.xs().tolist()
        
        # 过滤掉未检测到的基频点（值为0）
        filtered_data = [
            {"time": time, "frequency": freq} 
            for time, freq in zip(timestamps, pitch_values) 
            if freq > 0
        ]
        
        # 清理临时文件
        os.unlink(temp_path)
        
        if not filtered_data:
            return jsonify({
                "status": "error",
                "message": "未检测到有效的基频数据"
            }), 400
            
        result_data = {
            "status": "success",
            "data": filtered_data
        }
        
        # 对基频数据进行分析，获取归一化点和拟合参数
        analysis_result = analyze_audio_data(result_data)
        
        # 检查是否需要查询标准数据
        if 'character' in request.json and 'pinyin' in request.json:
            character = request.json.get('character')
            pinyin = request.json.get('pinyin')
            
            # 查询标准数据
            standard_audio = StandardAudioRepository.get_by_character_and_pinyin(character, pinyin)
            
            if standard_audio and standard_audio.get('frequency_data'):
                # 解析标准数据
                try:
                    standard_freq_data = json.loads(standard_audio['frequency_data'])
                    standard_analysis = analyze_audio_data(standard_freq_data)
                    
                    if standard_analysis.get('status') == 'success':
                        analysis_result['standard_data'] = {
                            'normalized_points': standard_analysis['normalized_points'],
                            'fit_params': standard_analysis['fit_params'],
                            'fit_curve_points': standard_analysis['fit_curve_points'],
                            'tone_type': standard_analysis['tone_type']
                        }
                except Exception as e:
                    analysis_result['standard_data_error'] = str(e)
        
        return jsonify(analysis_result)
        
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/standard-audios', methods=['GET'])
def get_standard_audios():
    """获取所有标准音频数据"""
    try:
        standard_audios = StandardAudioRepository.get_all()
        return jsonify({
            "status": "success",
            "data": standard_audios
        })
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/standard-audio/<character>/<pinyin>', methods=['GET'])
def get_standard_audio(character, pinyin):
    """获取指定汉字和拼音的标准音频数据"""
    try:
        standard_audio = StandardAudioRepository.get_by_character_and_pinyin(character, pinyin)
        
        if not standard_audio:
            return jsonify({
                "status": "error",
                "message": f"未找到汉字'{character}'拼音'{pinyin}'的标准音频数据"
            }), 404
            
        # 解析频率数据
        if standard_audio.get('frequency_data'):
            try:
                standard_audio['frequency_data'] = json.loads(standard_audio['frequency_data'])
            except:
                pass
        
        return jsonify({
            "status": "success",
            "data": standard_audio
        })
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/standard-audio/<int:audio_id>', methods=['GET'])
def get_standard_audio_by_id(audio_id):
    """根据ID获取标准音频数据"""
    try:
        standard_audio = StandardAudioRepository.get_by_id(audio_id)
        
        if not standard_audio:
            return jsonify({
                "status": "error",
                "message": f"未找到ID为{audio_id}的标准音频数据"
            }), 404
            
        # 解析频率数据
        if standard_audio.get('frequency_data'):
            try:
                standard_audio['frequency_data'] = json.loads(standard_audio['frequency_data'])
            except:
                pass
        
        return jsonify({
            "status": "success",
            "data": standard_audio
        })
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/standard-audio', methods=['POST'])
def save_standard_audio():
    """保存标准音频数据"""
    try:
        data = request.json
        
        # 验证必填字段
        required_fields = ['character_text', 'pinyin', 'tone_type']
        for field in required_fields:
            if field not in data:
                return jsonify({
                    "status": "error",
                    "message": f"缺少必填字段: {field}"
                }), 400
        
        # 获取字段值
        character_text = data.get('character_text')
        pinyin = data.get('pinyin')
        tone_type = data.get('tone_type')
        audio_path = data.get('audio_path', '')
        frequency_data = data.get('frequency_data', {})
        
        # 保存数据
        StandardAudioRepository.save_standard_audio(
            character_text, pinyin, tone_type, audio_path, frequency_data
        )
        
        return jsonify({
            "status": "success",
            "message": "标准音频数据保存成功"
        })
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/generate-tone-curve', methods=['POST'])
def generate_tone_curve():
    try:
        # 获取请求数据
        data = request.json
        if not data or 'data' not in data:
            return jsonify({'error': '未找到基频数据'}), 400
            
        # 声调类型（可选参数）
        tone_type = request.args.get('tone_type', '声调曲线分析')
            
        # 分析数据而不是绘制图像
        analysis_result = analyze_tone_data(data)
        
        return jsonify({
            "status": "success",
            "analysis": analysis_result
        })
        
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

def normalize_time(time_points):
    """将时间点归一化到0-1范围"""
    if not time_points:
        return []
        
    min_time = min(time_points)
    max_time = max(time_points)
    
    # 避免除以零
    if max_time == min_time:
        return [0.5] * len(time_points)
    
    return [(t - min_time) / (max_time - min_time) for t in time_points]

def compute_semitones(frequencies, ref_freq=None):
    """计算半音值"""
    if ref_freq is None:
        # 获取有效的最小值作为参考频率
        valid_freq = [f for f in frequencies if f > 0]
        if not valid_freq:
            return [0] * len(frequencies)
        ref_freq = min(valid_freq)
    
    # 应用半音公式: 12 * log2(f/f_ref)
    semitones = [12 * np.log2(f / ref_freq) if f > 0 else 0 for f in frequencies]
    return semitones

def analyze_tone_data(data):
    """分析声调数据，返回分析结果而不是图像"""
    # 提取频率和时间数据
    frequencies = [point["frequency"] for point in data["data"]]
    time_points = [point["time"] for point in data["data"]]
    
    # 过滤出有效的频率值
    valid_indices = [i for i, f in enumerate(frequencies) if f > 0]
    valid_frequencies = [frequencies[i] for i in valid_indices]
    valid_time_points = [time_points[i] for i in valid_indices]
    
    if not valid_frequencies:
        return {
            "status": "error",
            "message": "未检测到有效声调数据"
        }
    
    # 参考频率：使用最低有效频率
    ref_freq = min(valid_frequencies)
    
    # 归一化时间（0-1）
    min_time = min(valid_time_points)
    max_time = max(valid_time_points)
    normalized_time = [(t - min_time) / (max_time - min_time) if max_time > min_time else 0.5 
                       for t in valid_time_points]
    
    # 计算半音值: 12 * log2(f/f_ref)
    semitones = [12 * np.log2(f / ref_freq) for f in valid_frequencies]
    
    # 计算统计信息
    avg_semitone = np.mean(semitones)
    semitone_range = max(semitones) - min(semitones)
    
    # 根据半音范围识别声调类型
    tone_type = ""
    if semitone_range < 3:
        tone_type = "高平调"
    elif semitone_range >= 3 and semitone_range < 6:
        if semitones[-1] > semitones[0]:
            tone_type = "升调"
        else:
            tone_type = "降调"
    else:
        # 判断是否为降升调
        segments = len(semitones) // 3
        if segments > 0:
            first_segment = semitones[:segments]
            last_segment = semitones[-segments:]
            if np.mean(first_segment) > np.mean(semitones[segments:2*segments]) and np.mean(last_segment) > np.mean(semitones[segments:2*segments]):
                tone_type = "降升调"
            elif semitones[0] > semitones[-1]:
                tone_type = "全降调"
            else:
                tone_type = "起伏调"
    
    # 返回分析结果
    return {
        "normalized_points": [{"x": x, "y": y} for x, y in zip(normalized_time, semitones)],
        "stats": {
            "mean": float(avg_semitone),
            "min": float(min(semitones)),
            "max": float(max(semitones)),
            "range": float(semitone_range)
        },
        "tone_type": tone_type,
        "ref_freq": float(ref_freq)
    }

@app.route('/api/pcm-to-wav', methods=['POST'])
def pcm_to_wav():
    try:
        if 'audio' not in request.files:
            return jsonify({'error': '未找到音频文件'}), 400
            
        pcm_file = request.files['audio']
        sample_rate = int(request.form.get('sampleRate', 16000))
        channels = int(request.form.get('channels', 1))
        
        # 读取PCM数据
        pcm_data = pcm_file.read()
        
        # 创建临时WAV文件
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_wav:
            temp_wav_path = temp_wav.name
            
            # 创建WAV文件
            with wave.open(temp_wav_path, 'wb') as wav_file:
                wav_file.setnchannels(channels)
                wav_file.setsampwidth(2)  # 16位音频
                wav_file.setframerate(sample_rate)
                wav_file.writeframes(pcm_data)
        
        # 分析音频
        sound = parselmouth.Sound(temp_wav_path)
        pitch = sound.to_pitch()
        
        # 获取基频和时间信息
        pitch_values = pitch.selected_array['frequency'].tolist()
        timestamps = pitch.xs().tolist()
        
        # 过滤掉未检测到的基频点
        filtered_data = [
            {"time": time, "frequency": freq} 
            for time, freq in zip(timestamps, pitch_values) 
            if freq != 0
        ]
        
        # 清理临时文件
        os.unlink(temp_wav_path)
        
        return jsonify({
            "status": "success",
            "data": filtered_data
        })
        
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

if __name__ == '__main__':
    app.run(debug=True)
